using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Diagnostics;
using System.IO;
using System.Threading;

namespace ProcessChecker
{
    public partial class Form1 : Form
    {
        private ProcessList OldProcs;

        Dictionary<string, bool> NewProcs;

        private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();

        private Mutex timerMutex = new Mutex();

        public Form1()
        {
            InitializeComponent();

            if (Process.GetProcessesByName("ProcessChecker").Length > 1)
            {
                MessageBox.Show("There is another instance of Process Checker running, thus this one will be closed.", "Warning", MessageBoxButtons.OK);
                Process.GetCurrentProcess().Kill();
            }
            else
            {

                OldProcs = ReadSavedProcesses();

                PublishOldProcesses();

                PublishNewProcessAndUncheckedRunning(Process.GetProcesses());

                timer.Interval = int.Parse(Properties.Settings.Default.CheckPeriod);

                timer.Tick += new EventHandler(timer_Tick);

                timer.Enabled = true;
            }
        }

        void timer_Tick(object sender, EventArgs e)
        {
            timerMutex.WaitOne();
            PublishNewProcessAndUncheckedRunning(Process.GetProcesses());
            if (Properties.Settings.Default.AutoKill == true)
                KillUncheckedProcesses();
            timerMutex.ReleaseMutex();
        }

        private void PublishNewProcessAndUncheckedRunning(Process[] CurrentProcesses)
        {
            treeView1.Nodes.Clear();
            treeView3.Nodes.Clear();

            foreach (Process p in CurrentProcesses)
            {
                if (!OldProcs.ProcessList1.ContainsKey(p.ProcessName))
                {
                    TreeNode node = new TreeNode();
                    node.Text = p.ProcessName;
                    node.Checked = false;
                    try
                    {
                        node.Nodes.Add(p.MainModule.FileVersionInfo.FileDescription);
                    }
                    catch
                    {
                        node.Nodes.Add("");
                    }
                    treeView1.Nodes.Add(node);
                }
                else
                {
                    if (OldProcs.ProcessList1[p.ProcessName].Checked == false)
                    {
                        TreeNode node = new TreeNode();
                        node.Text = p.ProcessName;
                        node.Checked = false;
                        treeView3.Nodes.Add(node);
                        HistoryList.Items.Add(p.ProcessName);
                    }
                }
            }
        }

        private void PublishOldProcesses()
        {
            treeView2.Nodes.Clear();

            foreach (KeyValuePair<string, ProcessInfo> kv in OldProcs.ProcessList1)
            {
                TreeNode node = new TreeNode();
                node.Text = kv.Key;
                node.Checked = kv.Value.Checked;
                node.Nodes.Add(kv.Value.Description);
                treeView2.Nodes.Add(node);
            }
        }

        private ProcessList ReadSavedProcesses()
        {
            ProcessList Procsses;
            BinaryFormatter bf = new BinaryFormatter();
            try
            {
                FileStream fs = new FileStream("PCPs.prc3", FileMode.Open, FileAccess.Read);
                using (fs)
                {

                    Procsses = (ProcessList)bf.Deserialize(fs);

                }
            }
             catch
            {
                 Procsses = new ProcessList();
            }

            return Procsses;
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            DialogResult result = MessageBox.Show("Are you sure you want to close Process Checker?", "Warning", MessageBoxButtons.YesNo);

            if (result == DialogResult.Yes)
            {
                ValidateProcesses();

                if (Properties.Settings.Default.AutoKill == true)
                    KillUncheckedProcesses();

                SaveProcesses(OldProcs);
            }
            else
                e.Cancel = true;
        }

        private void KillUncheckedProcesses()
        {
            foreach (KeyValuePair<string, ProcessInfo> kv in OldProcs.ProcessList1)
            {
                if (kv.Value.Checked == false)
                {
                    foreach (Process p in Process.GetProcessesByName(kv.Key))
                    {
                        p.Kill();
                    }
                }
            }
        }

        private void ValidateProcesses()
        {
            OldProcs.ProcessList1.Clear();

            foreach (TreeNode n in treeView2.Nodes)
            {
                if (!OldProcs.ProcessList1.ContainsKey(n.Text))
                    OldProcs.ProcessList1.Add(n.Text, new ProcessInfo(n.Text, n.Nodes[0].Text, n.Checked));
            }

            foreach (TreeNode n in treeView1.Nodes)
            {
                if (!OldProcs.ProcessList1.ContainsKey(n.Text))
                    OldProcs.ProcessList1.Add(n.Text, new ProcessInfo(n.Text, n.Nodes[0].Text, n.Checked));
            }
        }

        private void SaveProcesses(ProcessList Procsses)
        {
            BinaryFormatter bf = new BinaryFormatter();
            FileStream fs = new FileStream("PCPs.prc3", FileMode.OpenOrCreate, FileAccess.Write);
            using (fs)
            {
                bf.Serialize(fs, Procsses);
            }
        }

        private void ValidateBtn_Click(object sender, EventArgs e)
        {
            timerMutex.WaitOne();
            ValidateProcesses();
            SaveProcesses(OldProcs);
            PublishOldProcesses();
            PublishNewProcessAndUncheckedRunning(Process.GetProcesses());
            timerMutex.ReleaseMutex();

            if (this.Text.Contains("*"))
                this.Text = this.Text.Remove(this.Text.Length - 1);
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            if (this.WindowState == FormWindowState.Minimized)
            {
                this.Hide();
            }
        }

        private void notifyIcon1_DoubleClick(object sender, EventArgs e)
        {
            this.Show();
            this.WindowState = FormWindowState.Normal;
        }

        private void treeView2_AfterCheck(object sender, TreeViewEventArgs e)
        {
            if (!this.Text.Contains("*"))
                this.Text = this.Text + "*";
        }

        private void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
        {
            if (!this.Text.Contains("*"))
                this.Text = this.Text + "*";
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Properties.Settings.Default.AutoKill = checkBox1.Checked;
            uint period = 0;
            if (uint.TryParse(PeriodBox.Text, out period))
            {
                Properties.Settings.Default.CheckPeriod = PeriodBox.Text;
                timer.Enabled = false;
                timer.Interval = int.Parse(Properties.Settings.Default.CheckPeriod);
                timer.Enabled = true;
            }
            else
            {
                MessageBox.Show("Invalid Period time, can't apply it.", "Error");
            }
            Properties.Settings.Default.Save();
        }
    }

    [Serializable]
    internal class ProcessList
    {
        private Dictionary<string, ProcessInfo> m_ProcessList;

        public Dictionary<string, ProcessInfo> ProcessList1
        {
            get { return m_ProcessList; }
            set { m_ProcessList = value; }
        }

        public ProcessList()
        {
            m_ProcessList = new Dictionary<string, ProcessInfo>();
        }
    }

    [Serializable]
    internal class ProcessInfo
    {
        private bool m_Checked;

        public bool Checked
        {
            get { return m_Checked; }
            set { m_Checked = value; }
        }
        private string m_Description;

        public string Description
        {
            get { return m_Description; }
            set { m_Description = value; }
        }

        private string m_Name;

        public string Name
        {
            get { return m_Name; }
            set { m_Name = value; }
        }

        public ProcessInfo(string pName, string pDescription, bool pChecked)
        {
            Name = pName;
            Description = pDescription;
            Checked = pChecked;
        }
    }
}